﻿using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

namespace Iot.Shared.Helper;

/// <summary>
///     枚举类型工具类，提供枚举值的缓存、显示名称获取等功能
///     使用线程安全的缓存机制提高性能
/// </summary>
public static class EnumHelper
{
    /// <summary>
    ///     枚举信息缓存字典
    ///     Key: 枚举类型
    ///     Value: 延迟加载的枚举信息缓存数据，确保线程安全初始化
    /// </summary>
    private static readonly ConcurrentDictionary<Type, Lazy<EnumTypeMetadata>> EnumMetadataCache = new();

    /// <summary>
    ///     获取指定枚举类型的所有值
    /// </summary>
    /// <typeparam name="TEnum">枚举类型</typeparam>
    /// <returns>枚举值的不可变数组</returns>
    /// <exception cref="ArgumentException">当TEnum不是枚举类型时抛出</exception>
    public static ImmutableArray<TEnum> GetValues<TEnum>() where TEnum : Enum
    {
        return [.. typeof(TEnum).GetEnumMetadata().Values.OfType<TEnum>()];
    }

    /// <summary>
    ///     获取指定类型的所有枚举值
    /// </summary>
    /// <param name="enumType">枚举类型</param>
    /// <returns>枚举值的不可变数组</returns>
    /// <exception cref="ArgumentException">当enumType不是枚举类型时抛出</exception>
    public static ImmutableArray<Enum> GetValues(this Type enumType)
    {
        return enumType.GetEnumMetadata().Values;
    }

    /// <summary>
    ///     获取枚举值的显示名称
    /// </summary>
    /// <param name="enumValue">枚举值</param>
    /// <returns>显示名称，如果未找到DisplayAttribute则返回枚举名称</returns>
    /// <exception cref="ArgumentException">当找不到枚举值的显示名称时抛出</exception>
    public static string GetDisplayName(this Enum enumValue)
    {
        var metadata = enumValue.GetType().GetEnumMetadata();
        return metadata.EnumDisplayNames.TryGetValue(enumValue, out var displayName)
            ? displayName
            : throw new ArgumentException($"未找到枚举值 {enumValue} 的显示名称");
    }

    public static bool IsDefined(this Enum value)
    {
        return value.GetType().IsDefined(value);
    }

    public static bool IsDefined(this Type enumType, object value)
    {
        return value switch
        {
            Enum enumValue => enumType.GetEnumMetadata().Values.Contains(enumValue),
            _ => false
        };
    }

    /// <summary>
    ///     枚举显示值转换到枚举值
    /// </summary>
    /// <param name="type">枚举类型</param>
    /// <param name="displayName">显示名称</param>
    /// <returns>枚举值</returns>
    /// <exception cref="ArgumentException"></exception>
    public static Enum DisplayNameToEnum(this Type type, string displayName)
    {
        var metadata = type.GetEnumMetadata();
        return metadata.DisplayNamesEnum.TryGetValue(displayName, out var value)
            ? value
            : throw new ArgumentException($"未找到枚举值 {displayName} 的显示名称");
    }

    /// <summary>
    ///     获取枚举类型所有值的显示名称字典
    /// </summary>
    /// <param name="enumType">枚举类型</param>
    /// <returns>冻结字典，Key为枚举值，Value为显示名称</returns>
    /// <exception cref="ArgumentException">当enumType不是枚举类型时抛出</exception>
    public static FrozenDictionary<Enum, string> GetEnumDisplayNames(this Type enumType)
    {
        return enumType.GetEnumMetadata().EnumDisplayNames;
    }

    /// <summary>
    ///     获取枚举值的排序顺序
    /// </summary>
    /// <param name="enumValue">枚举值</param>
    /// <returns>排序顺序，优先使用DisplayAttribute的Order，否则使用枚举的整数值</returns>
    public static int GetOrder(this Enum enumValue)
    {
        var metadata = enumValue.GetType().GetEnumMetadata();
        return metadata.ValueAttributes.TryGetValue(enumValue, out var attributeData)
            ? attributeData.Order
            : Convert.ToInt32(enumValue);
    }

    /// <summary>
    ///     从缓存中获取枚举类型的元数据，如果不存在则创建并缓存
    /// </summary>
    /// <param name="enumType">枚举类型</param>
    /// <returns>枚举类型的元数据</returns>
    /// <exception cref="ArgumentException">当enumType不是枚举类型时抛出</exception>
    private static EnumTypeMetadata GetEnumMetadata(this Type enumType)
    {
        ArgumentNullException.ThrowIfNull(enumType);

        return EnumMetadataCache.GetOrAdd(
                enumType,
                static type => new Lazy<EnumTypeMetadata>(() => CreateEnumMetadata(type)))
            .Value;
    }

    /// <summary>
    ///     创建枚举类型的元数据，包括值、显示属性等信息
    /// </summary>
    /// <param name="enumType">枚举类型</param>
    /// <returns>枚举类型的元数据</returns>
    /// <exception cref="ArgumentException">当enumType不是枚举类型时抛出</exception>
    private static EnumTypeMetadata CreateEnumMetadata(Type enumType)
    {
        if (!enumType.IsEnum)
        {
            throw new ArgumentException($"类型 {enumType.Name} 不是枚举类型", nameof(enumType));
        }

        var enumNames = enumType.GetEnumNames();
        var valueAttributes = new Dictionary<Enum, EnumValueMetadata>();

        // 遍历所有枚举名称，解析对应的值和显示属性
        foreach (var enumName in enumNames)
        {
            var enumValue = (Enum)Enum.Parse(enumType, enumName);
            var displayAttribute = enumType.GetField(enumName)?.GetCustomAttribute<DisplayAttribute>();

            valueAttributes.Add(enumValue, new EnumValueMetadata(enumValue, displayAttribute));
        }

        // 按Order排序枚举值
        var sortedValues = valueAttributes
            .OrderBy(static keyValuePair => keyValuePair.Value.Order)
            .Select(static keyValuePair => keyValuePair.Key).ToImmutableArray();
        var enumDisplayNames = valueAttributes.ToFrozenDictionary(static a => a.Key, static a => a.Value.DisplayName);
        return new EnumTypeMetadata(enumType, sortedValues, valueAttributes.ToFrozenDictionary())
        {
            EnumDisplayNames = enumDisplayNames,
            DisplayNamesEnum = enumDisplayNames.ToFrozenDictionary(static a => a.Value, static a => a.Key)
        };
    }

    /// <summary>
    ///     枚举类型元数据，包含类型信息和所有枚举值的属性数据
    /// </summary>
    /// <param name="EnumType">枚举类型</param>
    /// <param name="Values">排序后的枚举值集合</param>
    /// <param name="ValueAttributes">枚举值到其元数据的冻结字典</param>
    public sealed record EnumTypeMetadata(
        Type EnumType,
        ImmutableArray<Enum> Values,
        FrozenDictionary<Enum, EnumValueMetadata> ValueAttributes)
    {
        /// <summary>
        ///     枚举类型
        /// </summary>
        public Type EnumType { get; } = EnumType;

        /// <summary>
        ///     按显示顺序排序的枚举值集合
        /// </summary>
        public ImmutableArray<Enum> Values { get; } = Values;

        public required FrozenDictionary<Enum, string> EnumDisplayNames { get; init; }
        public required FrozenDictionary<string, Enum> DisplayNamesEnum { get; init; }


        /// <summary>
        ///     枚举值到其元数据的映射字典
        ///     使用冻结字典提供最优的读取性能
        /// </summary>
        public FrozenDictionary<Enum, EnumValueMetadata> ValueAttributes { get; } = ValueAttributes;
    }

    /// <summary>
    ///     单个枚举值的元数据，包含显示名称、排序顺序等信息
    /// </summary>
    public sealed class EnumValueMetadata
    {
        /// <summary>
        ///     初始化枚举值元数据
        /// </summary>
        /// <param name="enumValue">枚举值</param>
        /// <param name="displayAttribute">显示特性，可能为null</param>
        public EnumValueMetadata(Enum enumValue, DisplayAttribute? displayAttribute)
        {
            Value = enumValue;
            Name = enumValue.ToString();
            DisplayAttribute = displayAttribute;

            // 排序顺序：优先使用DisplayAttribute的Order，否则使用枚举的整数值
            Order = displayAttribute?.GetOrder() ?? Convert.ToInt32(enumValue);

            // 显示名称：优先使用DisplayAttribute的Name，否则使用枚举名称
            DisplayName = displayAttribute?.GetName() ?? Name;
        }

        /// <summary>
        ///     枚举值
        /// </summary>
        public Enum Value { get; }

        /// <summary>
        ///     枚举名称（字符串表示）
        /// </summary>
        public string Name { get; }

        /// <summary>
        ///     显示特性（可能为null）
        /// </summary>
        public DisplayAttribute? DisplayAttribute { get; }

        /// <summary>
        ///     显示名称，用于UI显示
        /// </summary>
        public string DisplayName { get; }

        /// <summary>
        ///     排序顺序
        /// </summary>
        public int Order { get; }

        /// <summary>
        ///     获取描述信息（如果DisplayAttribute有Description）
        /// </summary>
        public string? Description => DisplayAttribute?.GetDescription();

        /// <summary>
        ///     获取分组名称（如果DisplayAttribute有GroupName）
        /// </summary>
        public string? GroupName => DisplayAttribute?.GetGroupName();
    }
}